/*
 * Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved.
 * Description:Initial Draft
 * Author: Hisilicon multimedia software group
 * Create: 2020/02/26
 */

#include "hi_sample_syslink.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <asm/io.h>
#include <sys/time.h>
#include "securec.h"
#include "time.h"

#define CLK_GROUP_NUM 8
#define CLK_BIT_NUM 7
#define CMD_GROUP_NUM 8
#define CMD_BIT_NUM 5
#define INT_GROUP_NUM 7
#define INT_BIT_NUM 2

#define SAMPLE_MAX_MSG_LEN 480
#define SAMPLE_USEC_PER_SEC 1000000

typedef hi_u32 (*sample_callback)(hi_u32 argc, hi_char **argv);
typedef struct {
    const hi_char *cmd;
    sample_callback callback;
} sample_callback_table;

static hi_u32 sample_syslink_help(hi_u32 argc, hi_char **argv);

static hi_void print_boot_info(const hi_syslink_boot_info *boot_info)
{
    if (boot_info == HI_NULL) {
        printf("boot info null\n");
        return;
    }
    hi_u32 time;
    if (boot_info->len == sizeof(time)) {
        errno_t err = memcpy_s(&time, sizeof(time), boot_info->info, boot_info->len);
        if (err != EOK) {
            printf("memcpy_s err %d\n", err);
            return;
        }
        struct tm local;
        struct tm *ret;
        ret = localtime_r((time_t *)&time, &local);
        if (ret != HI_NULL) {
            printf("time:%d/%d/%d %02d:%02d:%02d\n",
                local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,   /* 1900 base year */
                local.tm_hour, local.tm_min, local.tm_sec);
        }
    } else {
        printf("len %hhu\n", boot_info->len);
        for (hi_u8 i = 0; i < boot_info->len; i++) {
            printf("%02x ", boot_info->info[i]);
        }
        printf("\n");
    }
}

static hi_void hisyslink_callback(hi_syslink_callback_type type, hi_syslink_callback_args *args)
{
    if (type == HI_SYSLINK_BOOT_INFO) {
        print_boot_info((hi_syslink_boot_info *)args);
    } else if (type == HI_SYSLINK_PIN_MUX_GPIO) {
        printf("HI_SYSLINK_PIN_MUX_GPIO\n");
        writel(0x1a00, 0x112c0048); /* gpio8_7 */
        writel(0x1000, 0x112c004C); /* gpio8_5 */
    } else if (type == HI_SYSLINK_PIN_MUX_SDIO) {
        printf("HI_SYSLINK_PIN_MUX_SDIO\n");
        writel(0x1D54, 0x112c0048); /* sdio1 clk */
        writel(0x0134, 0x112c0064); /* sdio1 data0 */
        writel(0x0134, 0x112c004C); /* sdio1 cmd */
        writel(0x0000, 0x112C0060); /* sdio1 data1 use as interrupt */
    } else if (type == HI_SYSLINK_NET_CHANGED) {
        hi_syslink_net_changed *net_changed = (hi_syslink_net_changed *)args;
        if (net_changed == HI_NULL) {
            return;
        }
        if (*net_changed == HI_SYSLINK_NET_IPV4_READY) {
            printf("ipv4 ready\n");
        } else if (*net_changed == HI_SYSLINK_NET_IPV4_NOT_READY) {
            printf("ipv4 not ready\n");
        } else if (*net_changed == HI_SYSLINK_NET_IPV6_READY) {
            printf("ipv6 ready\n");
        } else if (*net_changed == HI_SYSLINK_NET_IPV6_NOT_READY) {
            printf("ipv6 not ready\n");
        } else {
            printf("unknown net changed\n");
        }
    } else if (type == HI_SYSLINK_NET_CONFLICT) {
        printf("net conflict\n");
    } else if (type == HI_SYSLINK_ESTABLISHED) {
        printf("established\n");
    } else if (type == HI_SYSLINK_INTER_EXCEPTION) {
        printf("exception\n");
    } else {
        printf("unknown type %d\n", type);
    }
}

static hi_s32 hisyslink_msg_cb(hi_u32 msg_id, const hi_syslink_request *request, hi_syslink_response *response)
{
    hi_u32 i;
    if ((request == HI_NULL) || (response == HI_NULL)) {
        return -1;
    }
    if (msg_id == hi_syslink_msg_id_define(SAMPLE_SYSLINK_COMPONENT, SAMPLE_SYSLINK_MODULE, SAMPLE_SYSLINK_MSG)) {
        printf("request: ");
        for (i = 0; i < request->req_len; i++) {
            printf("0x%02x ", ((hi_u8*)request->request)[i]);
        }
        printf("\n");
        response->resp_len = SAMPLE_MSG_LEN;
        response->response = malloc(response->resp_len);
        if (response->response == HI_NULL) {
            response->resp_len = 0;
            return -1;
        }
        for (i = 0; i < response->resp_len; i++) {
            ((hi_u8*)response->response)[i] = i;
        }
        return 0;
    }
    printf("msg_id 0x%x\n", msg_id);
    return -1;
}

static hi_u32 sample_syslink_init(hi_u32 argc, hi_char **argv)
{
    hi_syslink_init_attr init_attr;
    init_attr.sdio_cfg.sdio_dev = 1;
    init_attr.sdio_cfg.sdio_pin.sdio_clk_out.group_number = CLK_GROUP_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_clk_out.bit_number = CLK_BIT_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_cmd.group_number = CMD_GROUP_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_cmd.bit_number = CMD_BIT_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_int.group_number = INT_GROUP_NUM;
    init_attr.sdio_cfg.sdio_pin.sdio_int.bit_number = INT_BIT_NUM;
    init_attr.callback = hisyslink_callback;
    hi_s32 ret = hi_syslink_init(&init_attr);
    if (ret != HI_SUCCESS) {
        printf("hi_syslink_init fail ret 0x%x\n", ret);
        return ret;
    }
    hi_u32 mod_id = hi_syslink_mod_id_define(SAMPLE_SYSLINK_COMPONENT, SAMPLE_SYSLINK_MODULE);
    hi_syslink_register_msg_cb(mod_id, hisyslink_msg_cb);
    printf("hisyslink sucess!\n");
    return 0;
}

void HI_syslink_init(void)
{
    sample_syslink_init(1, NULL);
}

static hi_u32 sample_syslink_deinit(hi_u32 argc, hi_char **argv)
{
    hi_s32 ret = hi_syslink_deinit();
    if (ret != HI_SUCCESS) {
        printf("hi_syslink_deinit fail ret 0x%x\n", ret);
        return ret;
    }
    return 0;
}

static hi_u32 sample_syslink_bootinfo(hi_u32 argc, hi_char **argv)
{
    hi_syslink_boot_info boot_info;
    hi_s32 ret = hi_syslink_get_boot_info(&boot_info);
    if (ret != HI_SUCCESS) {
        printf("hi_syslink_get_boot_info fail ret 0x%x\n", ret);
        return ret;
    }
    print_boot_info(&boot_info);
    return 0;
}

static hi_u32 sample_syslink_sendmsg(hi_u32 argc, hi_char **argv)
{
    hi_u32 i;
    hi_syslink_request request;
    hi_syslink_response response;
    hi_u8 req[SAMPLE_MSG_LEN] = { 0 };
    hi_u8 resp[SAMPLE_MSG_LEN] = { 0 };
    request.request = req;
    request.req_len = sizeof(req);
    response.response = resp;
    response.resp_len = sizeof(resp);
    for (i = 0; i < sizeof(req); i++) {
        req[i] = sizeof(req) - i;
    }
    hi_u32 msg_id = hi_syslink_msg_id_define(SAMPLE_SYSLINK_COMPONENT, SAMPLE_SYSLINK_MODULE, SAMPLE_SYSLINK_MSG);
    const hi_u32 timeout = 1000;
    hi_s32 ret = hi_syslink_send_msg_sync(msg_id, &request, &response, timeout);
    if (ret != HI_SUCCESS) {
        printf("hi_syslink_send_msg_sync fail ret 0x%x\n", ret);
        return ret;
    }
    printf("resp: ");
    for (i = 0; i < sizeof(resp); i++) {
        printf("0x%02x ", resp[i]);
    }
    printf("\n");
    return 0;
}

static hi_syslink_tty_type str_to_tty_type(const hi_char *tty_name)
{
    if (strcmp(tty_name, "shell") == 0) {
        return HI_SYSLINK_TTY_SHELL;
    } else if (strcmp(tty_name, "at") == 0) {
        return HI_SYSLINK_TTY_AT;
    } else if (strcmp(tty_name, "diag") == 0) {
        return HI_SYSLINK_TTY_DIAG;
    } else if (strcmp(tty_name, "sigma") == 0) {
        return HI_SYSLINK_TTY_SIGMA;
    } else {
        return HI_SYSLINK_TTY_BUTT;
    }
}

static hi_u32 sample_syslink_enable_tty(hi_u32 argc, hi_char **argv)
{
    if (argc < 1) {
        printf("example: enabletty shell/at/diag/sigma\n");
        return 0;
    }
    hi_syslink_tty_type type = str_to_tty_type(argv[0]);
    if (type == HI_SYSLINK_TTY_BUTT) {
        printf("example: enabletty shell/at/diag/sigma\n");
        return 0;
    }

    return hi_syslink_enable_tty(type, HI_TRUE);
}

static hi_u32 sample_syslink_disable_tty(hi_u32 argc, hi_char **argv)
{
    if (argc < 1) {
        printf("example: disabletty shell/at/diag/sigma\n");
        return 0;
    }
    hi_syslink_tty_type type = str_to_tty_type(argv[0]);
    if (type == HI_SYSLINK_TTY_BUTT) {
        printf("example: disabletty shell/at/diag/sigma\n");
        return 0;
    }

    return hi_syslink_enable_tty(type, HI_FALSE);
}


static hi_u32 sample_syslink_attach_tty(hi_u32 argc, hi_char **argv)
{
    if (argc < 1) {
        printf("example: attachtty shell/at/diag/sigma\n");
        return 0;
    }
    hi_syslink_tty_type type = str_to_tty_type(argv[0]);
    if (type == HI_SYSLINK_TTY_BUTT) {
        printf("example: attachtty shell/at/diag/sigma\n");
        return 0;
    }

    return hi_syslink_attach_tty(type);
}

static hi_u32 sample_syslink_set_log_level(hi_u32 argc, hi_char **argv)
{
    if (argc < 1) {
        printf("example: setloglevel 2\n");
        return 0;
    }

    return hi_syslink_set_log_level(atoi(argv[0]));
}

static hi_u32 sample_syslink_get_log_level(hi_u32 argc, hi_char **argv)
{
    hi_syslink_log_level log_level;

    hi_syslink_get_log_level(&log_level);
    printf("log_level %d\n", log_level);

    return 0;
}

static hi_u32 sample_syslink_send_update(hi_u32 argc, hi_char **argv)
{
    hi_s32 ret = 0;
    struct timeval start, end;
    hi_u32 total_len = 0x100000;    /* 0x100000 1M */
    hi_syslink_request request;
    hi_syslink_response response = { HI_NULL, 0 };
    hi_u32 msg_id =
        hi_syslink_msg_id_define(SAMPLE_SYSLINK_COMPONENT, SAMPLE_SYSLINK_MODULE, SAMPLE_SYSLINK_MSG_UPDATE);
    const hi_u32 timeout = 1000;
    request.request = malloc(SAMPLE_MAX_MSG_LEN);
    if (request.request == HI_NULL) {
        printf("malloc fail\n");
        return -1;
    }
    request.req_len = SAMPLE_MAX_MSG_LEN;
    gettimeofday(&start, HI_NULL);
    while (total_len >= SAMPLE_MAX_MSG_LEN) {
        ret = hi_syslink_send_msg_sync(msg_id, &request, &response, timeout);
        if (ret != HI_SUCCESS) {
            printf("hi_syslink_send_msg_sync fail ret 0x%x\n", ret);
            break;
        }
        total_len -= SAMPLE_MAX_MSG_LEN;
    }
    if ((total_len) > 0 && (total_len < SAMPLE_MAX_MSG_LEN)) {
        request.req_len = total_len;
        ret = hi_syslink_send_msg_sync(msg_id, &request, &response, timeout);
        if (ret != HI_SUCCESS) {
            printf("hi_syslink_send_msg_sync fail ret 0x%x\n", ret);
        }
    }
    gettimeofday(&end, HI_NULL);
    time_t cost = (end.tv_sec - start.tv_sec) * SAMPLE_USEC_PER_SEC + end.tv_usec - start.tv_usec;
    const double speed = SAMPLE_USEC_PER_SEC / (double)cost;
    printf("cost %ld us\n", cost);
    printf("speed %f MByte/s\n", speed);
    free(request.request);
    return ret;
}

static sample_callback_table g_syslink_table[] = {
    { "help", sample_syslink_help },
    { "init", sample_syslink_init },
    { "deinit", sample_syslink_deinit },
    { "bootinfo", sample_syslink_bootinfo },
    { "sendmsg", sample_syslink_sendmsg },
    { "enabletty", sample_syslink_enable_tty },
    { "disabletty", sample_syslink_disable_tty },
    { "attachtty", sample_syslink_attach_tty },
    { "setloglevel", sample_syslink_set_log_level },
    { "getloglevel", sample_syslink_get_log_level },
    { "sendupdate", sample_syslink_send_update },
};

static hi_u32 sample_syslink_help(hi_u32 argc, hi_char **argv)
{
    hi_u32 i;
    printf("hisyslink support cmd: ");
    for (i = 0; i < sizeof(g_syslink_table) / sizeof(g_syslink_table[0]); i++) {
        printf("%s ", g_syslink_table[i].cmd);
    }
    printf("\n");
    return 0;
}

hi_u32 hi_syslink_sample(hi_u32 argc, hi_char **argv)
{
    if (argc < 1 || argv == HI_NULL) {
        sample_syslink_help(argc, argv);
        return 0;
    }

    hi_u32 i, ret;
    for (i = 0; i < sizeof(g_syslink_table) / sizeof(g_syslink_table[0]); i++) {
        if (strcmp(argv[0], g_syslink_table[i].cmd) == 0) {
            ret = g_syslink_table[i].callback(argc - 1, &argv[1]);
            if (ret == 0) {
                printf("%s success.\n", g_syslink_table[i].cmd);
            } else {
                printf("%s failure.\n", g_syslink_table[i].cmd);
            }
            return ret;
        }
    }
    return sample_syslink_help(argc - 1, &argv[1]);
}